home *** CD-ROM | disk | FTP | other *** search
- /*-
-
- FlippyFloppyView.m
-
- Warp, twist, and bounce a piece of text around the screen.
-
- Copyright (c) 1992 by Scott Byer
-
- Permission is given to freely distribute and modify this code
- provided the copyright notice is left unchanged.
- No warranties, explicit or implied, apply to this code.
- */
-
- #import "FlippyFloppyView.h"
-
- #import <appkit/appkit.h>
-
- #import <dpsclient/dpsclient.h>
- #import <dpsclient/psops.h>
-
- #import <libc.h>
- #import <stdlib.h>
-
- #include "FlippyFloppyWraps.h"
-
- #define XMOVEFACTOR (0.1 * hyperness)
- #define YMOVEFACTOR (0.1 * hyperness)
-
- #define XSPEEDMAX (20.0 * hyperness)
- #define YSPEEDMAX (20.0 * hyperness)
- #define XSPEEDMIN (-20.0 * hyperness)
- #define YSPEEDMIN (-20.0 * hyperness)
-
- #define ROTATEFACTOR (0.5 * hyperness)
-
- #define ROTATESPEEDMAX (20.0 * hyperness)
- #define ROTATESPEEDMIN (-20.0 * hyperness)
-
- #define XSCALEFACTOR (0.05 * hyperness)
- #define YSCALEFACTOR (0.05 * hyperness)
-
- #define XSCALESPEEDMAX (0.1 * hyperness)
- #define YSCALESPEEDMAX (0.1 * hyperness)
- #define XSCALESPEEDMIN (-0.1 * hyperness)
- #define YSCALESPEEDMIN (-0.1 * hyperness)
-
- #define XSCALEMIN -5.0
- #define YSCALEMIN -5.0
- #define XSCALEMAX 5.0
- #define YSCALEMAX 5.0
-
- #define SCALENOMANSLAND 0.08
-
- #define COLORFACTOR (0.01 * hyperness)
-
- #define COLORSPEEDMAX (0.05 * hyperness)
- #define COLORSPEEDMIN (-0.05 * hyperness)
-
- #define COLORMIN 0.1
- #define COLORMAX 1.0
-
- @implementation FlippyFloppyView
-
- #define DRANDOM(factor) ((random() / 2147483647.0) * (factor) - ((factor) / 2.0))
-
- - oneStep
- {
- int whichLine;
-
- /* Erase the old text by doing a transform and drawing a black
- rectangle. */
- FFWTransform(x, y, xscale, yscale, rotation);
- FFWBlackBox(stringsBBox.llx, stringsBBox.lly,
- stringsBBox.urx, stringsBBox.ury);
- FFWDeTransform(-x, -y, 1.0/xscale, 1.0/yscale, -rotation);
-
- /* If the font or the text to display has changed, go figure out what
- is now different, and act accordingly. */
- if (changed) {
- [self somethingChanged];
- changed = NO;
- }
-
- /* Sanity check to make sure we have at least one string to display. */
- if (numLines == 0) return self;
-
- /* Change the movemoent of the characters */
- xspeed += DRANDOM(XMOVEFACTOR);
- yspeed += DRANDOM(YMOVEFACTOR);
-
- /* Bounds checking for the movement speed. */
- if (xspeed < XSPEEDMIN) xspeed = XSPEEDMIN;
- if (xspeed > XSPEEDMAX) xspeed = XSPEEDMAX;
-
- if (yspeed < YSPEEDMIN) yspeed = YSPEEDMIN;
- if (yspeed > YSPEEDMAX) yspeed = YSPEEDMAX;
-
- /* Change to location of the characters based on the new movement. */
- x += xspeed;
- y += yspeed;
-
- /* Bounds checking for the absolute location. */
- if (x < 0.0) {
- x = 0.0;
- xspeed = -xspeed;
- }
- if (x > xmax) {
- x = xmax;
- xspeed = -xspeed;
- }
-
- if (y < 0.0) {
- y = 0.0;
- yspeed = -yspeed;
- }
- if (y > ymax) {
- y = ymax;
- yspeed = -yspeed;
- }
-
- /* Change the rate of rotation. */
- rotationspeed += DRANDOM(ROTATEFACTOR);
-
- /* Bounds check the rotation rate. */
- if (rotationspeed < ROTATESPEEDMIN) rotationspeed = ROTATESPEEDMIN;
- if (rotationspeed > ROTATESPEEDMAX) rotationspeed = ROTATESPEEDMAX;
-
- /* Add the rotation in. */
- rotation += rotationspeed;
-
- /* Bounds check the rotation angle. */
- if (rotation < 0.0) rotation += 360.0;
- if (rotation > 360.0) rotation -= 360.0;
-
- /* Change the rate of scaling. */
- xscalespeed += DRANDOM(XSCALEFACTOR);
- yscalespeed += DRANDOM(YSCALEFACTOR);
-
- /* Check the scale rate agains the boundaries. */
- if (xscalespeed < XSCALESPEEDMIN) xscalespeed = XSCALESPEEDMIN;
- if (xscalespeed > XSCALESPEEDMAX) xscalespeed = XSCALESPEEDMAX;
-
- if (yscalespeed < YSCALESPEEDMIN) yscalespeed = YSCALESPEEDMIN;
- if (yscalespeed > YSCALESPEEDMAX) yscalespeed = YSCALESPEEDMAX;
-
- /* Check the scaling factor. */
- xscale += xscalespeed;
- yscale += yscalespeed;
-
- /* Work around what seemed like a rasteriation bug in 3.0. */
- while ((xscale > -SCALENOMANSLAND) && (xscale < SCALENOMANSLAND))
- xscale += xscalespeed;
- while ((yscale > -SCALENOMANSLAND) && (yscale < SCALENOMANSLAND))
- yscale += yscalespeed;
-
- /* Bounds check the scaling. */
- if (xscale < XSCALEMIN) {
- xscale = XSCALEMIN;
- xscalespeed = -xscalespeed;
- }
- if (xscale > XSCALEMAX) {
- xscale = XSCALEMAX;
- xscalespeed = -xscalespeed;
- }
-
- if (yscale < YSCALEMIN) {
- yscale = YSCALEMIN;
- yscalespeed = -yscalespeed;
- }
- if (yscale > YSCALEMAX) {
- yscale = YSCALEMAX;
- yscalespeed = -yscalespeed;
- }
-
- /* Calculate the new color bounce speeds. */
- rspeed += DRANDOM(COLORFACTOR);
- gspeed += DRANDOM(COLORFACTOR);
- bspeed += DRANDOM(COLORFACTOR);
-
- /* Bounds check the color speeds. */
- if (rspeed < COLORSPEEDMIN) rspeed = COLORSPEEDMIN;
- if (rspeed > COLORSPEEDMAX) rspeed = COLORSPEEDMAX;
-
- if (gspeed < COLORSPEEDMIN) gspeed = COLORSPEEDMIN;
- if (gspeed > COLORSPEEDMAX) gspeed = COLORSPEEDMAX;
-
- if (bspeed < COLORSPEEDMIN) bspeed = COLORSPEEDMIN;
- if (bspeed > COLORSPEEDMAX) bspeed = COLORSPEEDMAX;
-
- /* Change the colors by the speeds. */
- r += rspeed;
- g += gspeed;
- b += bspeed;
-
- /* Bounds check the color. */
- if (r < COLORMIN) {
- r = COLORMIN;
- rspeed = -rspeed;
- }
- if (r > COLORMAX) {
- r = COLORMAX;
- rspeed = -rspeed;
- }
-
- if (g < COLORMIN) {
- g = COLORMIN;
- gspeed = -gspeed;
- }
- if (g > COLORMAX) {
- g = COLORMAX;
- gspeed = -gspeed;
- }
-
- if (b < COLORMIN) {
- b = COLORMIN;
- bspeed = -bspeed;
- }
- if (b > COLORMAX) {
- b = COLORMAX;
- bspeed = -bspeed;
- }
-
- /* Transform the coordinate system and draw the text. */
- FFWTransform(x, y, xscale, yscale, rotation);
- PSsetrgbcolor(r, g, b);
- for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
- FFWText(startStrings[whichLine].x, startStrings[whichLine].y,
- displayStrings[whichLine]);
- }
- FFWDeTransform(-x, -y, 1.0/xscale, 1.0/yscale, -rotation);
-
- return self;
- }
-
- - didLockFocus
- {
- [textFont set];
- return self;
- }
-
- - initFrame:(NXRect *)frameRect
- {
- [super initFrame:frameRect];
-
- [inspectorPanel display];
- [self newSize];
- [self initValues];
-
- srandom(time(0));
- return self;
- }
-
- -initValues
- {
- /* Initialize the position. */
- x = xmax / 2.0;
- y = ymax / 2.0;
- r = g = b = 0.5;
- rotation = 5.0;
- xscale = yscale = 1.0;
-
- /* Initialize the rate of change */
- xspeed = yspeed = 5.0;
- rotationspeed = 10.0;
-
- /* Initialize the overall control factor. */
- hyperness = 1.0;
-
- return self;
- }
-
- - inspector:sender
- {
- static NXDefaultsVector FlippyFloppyDefaults = {
- { "NXFont", "Times-Roman" },
- { "Phrase", "Flippy\nFloppy" },
- { NULL, NULL }};
-
- char buf[MAXPATHLEN];
-
- if (!inspectorPanel) {
- sprintf(buf,"%s/FlippyFloppy.nib",[sender
- moduleDirectory:"FlippyFloppy"]);
- [NXApp loadNibFile:buf owner:self withNames:NO];
-
- /* Register our defaults. */
- NXRegisterDefaults("BackSpace.FlippyFloppy", FlippyFloppyDefaults);
-
- changed = YES;
- }
-
- return inspectorPanel;
- }
-
- /* Attach ourselves to the end of a window's responder chain. */
- - add:someone ToResponderListOf: whichWindow
- {
- id prevResp, curResp;
-
- /* Put ourselves at the end of a window's responder chain. */
- prevResp = curResp = whichWindow;
- while ((curResp != someone) && (curResp != nil)) {
- /* Guarunteed to go through once. */
- prevResp = curResp;
- curResp = [curResp nextResponder];
- }
- if (curResp == nil) [prevResp setNextResponder:someone];
- return self;
- }
-
- /* Remove ourselves from anywhere within a window's responder chain. */
- - remove:someone FromResponderListOf: whichWindow
- {
- id prevResp, curResp;
-
- /* Remove ourselve from the middle or end of a responder chain. */
- prevResp = curResp = whichWindow;
- while ((curResp != someone) && (curResp != nil)) {
- /* Guarunteed to go through once. */
- prevResp = curResp;
- curResp = [curResp nextResponder];
- }
- if (curResp == someone) [prevResp setNextResponder:
- [someone nextResponder]];
- return self;
- }
-
-
- - inspectorInstalled
- {
- const char *fontName, *phrase;
-
- /* Get ourselves access to the font panel. */
- fontManager = [FontManager new];
- fontPanel = [fontManager getFontPanel:YES];
-
- /* We better not have a nextResponder before placing ourselves in
- the middle of these responder lists. */
- [self setNextResponder:nil];
-
- /* Get ourselves placed at the end of the responder chains for the
- windows we are dealing with. */
- [self add:self ToResponderListOf:fontPanel];
-
- /* Make the font panel a responder of the other two windows. */
- [self add:fontPanel ToResponderListOf:[inspectorPanel window]];
- [self add:fontPanel ToResponderListOf:[self window]];
-
- /* Make the FontManager use this module's action message. */
- [fontManager setAction:@selector(changeFontFlippyFloppy:)];
-
- /* Get the default font from the defaults database. */
- fontName = NXGetDefaultValue("BackSpace.FlippyFloppy", "NXFont");
- textFont = [Font newFont:fontName size:24 matrix:NX_IDENTITYMATRIX];
- [fontPanel setPanelFont:textFont isMultiple:NO];
- [fontNameDisplay setStringValue:[textFont displayName]];
- [flippyFloppyButton display];
-
- /* Get the default phrase from the database. */
- phrase = NXGetDefaultValue("BackSpace.FlippyFloppy", "Phrase");
- [textPanel setStringValue:phrase];
-
- changed = YES;
-
- return self;
- }
-
- - inspectorWillBeRemoved
- {
- /* Hide the font panel. */
- [fontPanel orderWindow:NX_OUT relativeTo:0];
-
- /* Make the FontManager use the generic action message. */
- [fontManager setAction:@selector(changeFont:)];
-
- /* Remove the fontpanel from the other window's responder lists.
- Remove ourselves from the fontPanel's responder list. */
- [self remove:self FromResponderListOf:fontPanel];
- [self remove:fontPanel FromResponderListOf:[inspectorPanel window]];
- [self remove:fontPanel FromResponderListOf:[self window]];
-
- /* Restore the cache limit parameter for the context. */
- if (cacheLimit == 0) cacheLimit = 12500;
- FFWsetcachelimit(cacheLimit);
-
- return self;
- }
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- [super sizeTo:width :height];
- [self newSize];
- return self;
- }
-
- - newSize
- {
- xmax = bounds.size.width;
- ymax = bounds.size.height;
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- if (!rects || !rectCount)
- {
- return self;
- }
- PSsetgray(0.0);
- NXRectFill(rects);
- return self;
- }
-
- - (BOOL)useBufferedWindow
- {
- return YES;
- }
-
- - (const char *)windowTitle
- {
- return "Make FlippyFloppy!";
- }
-
- - setHyperness: sender
- {
- hyperness = [sender floatValue];
- [hypernessField setFloatValue:hyperness];
- return self;
- }
-
- - setFlippyText: sender
- {
- [textPanel selectText:self];
- NXWriteDefault("BackSpace.FlippyFloppy", "Phrase", [textPanel stringValue]);
- changed = YES;
- return self;
- }
-
- - changeFontFlippyFloppy: sender
- {
- textFont = [sender convertFont:textFont];
- NXWriteDefault("BackSpace.FlippyFloppy", "NXFont", [textFont name]);
- [fontNameDisplay setStringValue:[textFont displayName]];
- [flippyFloppyButton display];
- changed = YES;
- return sender;
- }
-
- - setFloppyFont: sender
- {
- /* Make the FontManager use this module's action message. */
- [fontManager setAction:@selector(changeFontFlippyFloppy:)];
-
- /* Make the font panel appear! */
- [[fontPanel setPanelFont:textFont isMultiple:NO]
- makeKeyAndOrderFront:[inspectorPanel window]];
-
- return self;
- }
-
- - somethingChanged
- {
- const char *textToSplit, *splitStart;
- int whichLine, oldCacheLimit;
- TextBBox linesBBox[MAX_TEXT_LINES];
- double tweak, baseline;
-
- /* Zero out the cache limit - this is a *per context* variable (which is
- why we do it here - to make *sure* the context we are flipping text in
- has it temporarily zeroed out. */
- FFWzerocachelimit(&oldCacheLimit);
- if (oldCacheLimit != 0) cacheLimit = oldCacheLimit;
-
- /* Make sure we are using the right font. */
- [textFont set];
- [fontNameDisplay setStringValue:[textFont displayName]];
- [flippyFloppyButton display];
-
- /* Free the previous chunks of text, if any. */
- for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
- if (displayStrings[whichLine] != NULL) {
- (void)free(displayStrings[whichLine]);
- displayStrings[whichLine] = NULL;
- }
- }
-
- /* Figure out how many lines there are to display, keeping track of where
- each line begins. */
- whichLine = 0;
- textToSplit = [textPanel stringValue];
- if (textToSplit == NULL) return NULL;
-
- do {
- splitStart = textToSplit;
- while ((*textToSplit != 0) && (*textToSplit != '\n'))
- textToSplit++;
-
- /* Allocate the string chunk and copy the right part of the string. */
- displayStrings[whichLine] = malloc(textToSplit - splitStart + 1);
- if (displayStrings[whichLine] != NULL) {
- strncpy(displayStrings[whichLine], splitStart,
- textToSplit - splitStart);
- displayStrings[whichLine][textToSplit-splitStart] = 0;
- whichLine++;
- }
-
- /* Turn returns into string endings. */
- if (*textToSplit == '\n') textToSplit++;
-
- } while ((*textToSplit != 0) && (whichLine < MAX_TEXT_LINES));
-
- numLines = whichLine;
-
- /* Figure out the bounding boxes for each string, and at the same time
- what the total bounding box will be. */
- stringsBBox.llx = 100.0;
- stringsBBox.urx = -100.0;
- stringsBBox.lly = 0.0;
- stringsBBox.ury = 0.0;
-
- for (whichLine = 0 ; whichLine < numLines ; whichLine++) {
- FFWTextBBox(displayStrings[whichLine],
- &linesBBox[whichLine].llx, &linesBBox[whichLine].lly,
- &linesBBox[whichLine].urx, &linesBBox[whichLine].ury);
-
- /* The strings are centered over one another in the x direction. */
- if (linesBBox[whichLine].llx < stringsBBox.llx)
- stringsBBox.llx = linesBBox[whichLine].llx;
- if (linesBBox[whichLine].urx > stringsBBox.urx)
- stringsBBox.urx = linesBBox[whichLine].urx;
-
- /* The strings sit on top of one another in the y direction. */
- stringsBBox.lly += linesBBox[whichLine].lly;
- stringsBBox.ury += linesBBox[whichLine].ury + 2.0;
- }
-
- /* Now, tweak the bounding box to reflect the desire to have it centered
- around the origin. */
- tweak = (stringsBBox.llx + stringsBBox.urx) / 2.0;
- stringsBBox.llx -= tweak;
- stringsBBox.urx -= tweak;
-
- tweak = (stringsBBox.lly + stringsBBox.ury) / 2.0;
- stringsBBox.lly -= tweak;
- stringsBBox.ury -= tweak;
-
- /* Now, for each line of text, figure out where it's starting point is
- going to be. */
- baseline = stringsBBox.ury;
- for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
-
- tweak = (linesBBox[whichLine].llx + linesBBox[whichLine].urx) / 2.0;
- startStrings[whichLine].x = -tweak;
-
- baseline -= (linesBBox[whichLine].ury + 2.0);
- startStrings[whichLine].y = baseline;
- }
-
- /* Bump out the bounding box slightly. */
- stringsBBox.llx -= 6.0; stringsBBox.lly -= 6.0;
- stringsBBox.urx += 6.0; stringsBBox.ury += 6.0;
-
- /* Convert the bounding box into a rectangle. */
- stringsBBox.urx -= stringsBBox.llx;
- stringsBBox.ury -= stringsBBox.lly;
-
- return self;
- }
-
- @end
-
-